import Utils from './utils'
/**
 * Object类, 用于创建leancloud数据.
 * @constructor
 * @param {string} className - 需要创建的class名.
 */
class $Object {
  constructor(AV, className, defaultACL, request, that) {
    this.AV = AV
    this.$http = className ? new AV.Object(className) : AV.Object
    this.defaultACL = defaultACL
    this.request = request
    this.that = that
    this.avObject = null
  }

  // 保存
  save() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () => (avObject ? avObject.save() : $http.save())
    return request.call(that, dispatchRequest)
  }

  saveAll(objects) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.saveAll(objects)
    return request.call(that, dispatchRequest)
  }

  destroy() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () =>
      avObject ? avObject.destroy() : $http.destroy()
    return request.call(that, dispatchRequest)
  }

  destroyAll(objectIds) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.destroyAll(objectIds)
    return request.call(that, dispatchRequest)
  }

  fetch() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () => (avObject ? avObject.fetch() : $http.fetch())
    return request.call(that, dispatchRequest)
  }

  fetchAll(objectIds) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.fetchAll(objectIds)
    return request.call(that, dispatchRequest)
  }

  increment(key, count) {
    const { $http, avObject } = this
    const http = avObject || $http
    this.avObject = request.increment(key, count)
    return this
  }

  extend(classname) {
    if (!classname) return null
    return this.$http.extend(classname)
  }

  getUsers() {
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.getUsers()
    this.avObject = request
    return this
  }

  add(key, item) {
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.add(key, item)
    this.avObject = request
    return this
  }

  createWithoutData(className, objectId) {
    const { AV, avObject, $http } = this
    this.avObject = AV.Object.createWithoutData(className, objectId)
    return this
  }

  set(jsonObj) {
    let request = this.$http
    if (this.avObject) request = this.avObject
    for (const k in jsonObj) {
      request.set(k, jsonObj[k])
    }
    this.avObject = request
    return this
  }

  unset(keys) {
    let request = this.$http
    if (!keys || !keys.length) return
    if (this.avObject) request = this.avObject
    keys.forEach((item) => request.unset(item))
    this.avObject = request
    return this
  }

  setACL(acl) {
    if (!acl) return
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.setACL(acl)
    this.avObject = request
    return this
  }

  // 个人定制API

  async handleData(data, { $pointType, $acl }) {
    const { AV, $http, avObject, that, defaultACL } = this
    const TYPE = $pointType
    const ACL = $acl
    const jsonObj = {
      ...data,
    }
    const setData = {}
    // acl对象
    if (defaultACL) this.setACL(defaultACL)
    Object.keys(jsonObj).forEach((item) => {
      if (
        jsonObj[item] &&
        (!Utils.isPlainObject(jsonObj[item]) || !jsonObj[item][TYPE]) &&
        item !== ACL
      ) {
        setData[item] = jsonObj[item]
      } else if (jsonObj[ACL] && Utils.isPlainObject(jsonObj[ACL])) {
        const aclObj = that.ACL().createACL({ ...jsonObj[ACL] }).avObject
        this.setACL(aclObj)
      }
    })
    // point指针对象
    const pointObjs = Object.keys(jsonObj)
      .filter(
        (item) => Utils.isPlainObject(jsonObj[item]) && jsonObj[item][TYPE]
      )
      .map((item) => ({
        [item]: jsonObj[item],
      }))
    // point指针搜索项
    const pointObjKeys = pointObjs.map((item) => Object.keys(item)[0])
    const findDatas = await Promise.all(
      pointObjs.map(async (item, index) => {
        const value = item[pointObjKeys[index]]
        const conditionKey = Object.keys(value).filter((item) => item !== TYPE)
        const qdata = new AV.Query(value[TYPE])
        const point = await qdata
          .equalTo([conditionKey[0]], value[conditionKey[0]])
          .first()
        return {
          key: pointObjKeys[index],
          point,
        }
      })
    )
    const pointDatas = findDatas
      .filter((item) => item.point)
      .map((item) => ({
        key: item.key,
        point: AV.Object.createWithoutData(item.point.className, item.point.id),
        className: item.point.className,
        id: item.point.id,
      }))
    pointDatas.forEach((item) => (setData[item.key] = item.point))
    return setData
  }

  /**
   * 构建或更新数据库行数据对象 (行对象)
   * @param {Object[]} datas - 需要保存的数据数组对象
   * @param {String} [datas[].objectId] - 需要修改的对象objectId
   * @param {Object} [datas[].acl] - 需要添加到对象的acl权限
   * @param {Object[]} [datas[].key] - 数据对象的key
   * @param {Object[]} datas[].key.pointType - 需要新建point的class名
   * @param {Object} [options] - 配置项及key名称修改
   * @param {String} options.$objectId - 需要修改的对象id属性名, 默认为 objectId
   * @param {String} options.$pointType - 生成point的type属性名,默认为 pointType
   * @param {String} options.$acl - acl的属性名称,详细信息请查看ACL对象文档,默认为 acl
   * @return {Array} LeanCloud数据对象数组
   *
   * @example
   * //创建一条带有分类point的图片对象数据
   * Object('Test').setData([{name: 'banner1', url: '../img/banner1.jpg', defaultCate: {pointType: 'Cate_Banner', name: 'homeBanner'}, acl:{writeRole: ['master', 'admin', 'test']}}])
   * 
   * //修改objectId为'628dee60fae7474740d3fbe8'的对象
   * Object('Test').setData([{objectId: '628dee60fae7474740d3fbe8',data: {name: 'banner1', defaultCate: {pointType: 'Cate_Banner', name: 'homeBanner'}, url: '../img/banner1.jpg'}}])
   *
   */

  async setData(
    datas,
    { $objectId = 'objectId', $pointType = 'pointType', $acl = 'acl' } = {}
  ) {
    if (!datas && datas.length) return
    const OBJECT_ID = $objectId
    const TYPE = $pointType
    const ACL = $acl
    const { AV, $http, avObject, that } = this
    const { className } = $http
    const AVObjcets = await Promise.all(
      datas.map(async (item) => {
        const hasId = item[OBJECT_ID]
        const data = hasId ? item.data : item
        const request = hasId
          ? this.createWithoutData(className, hasId).avObject
          : $http
        const setData = await this.handleData(data, {
          $pointType: TYPE,
          $acl: ACL,
        })
        request.set({ ...setData })
        return request
      })
    )
    return this.saveAll(AVObjcets)
  }
  /**
   * 批量删除数据库行数据对象或对象属性 (行对象)
   * @param {Array} objectIds 需要删除的数据对象id数组
   * @return null 返回为空
   */
  deleteData(objectIds) {
    const { AV, $http } = this
    const points = []
    const { className = '' } = $http
    if (!className) return
    if (!objectIds || !objectIds.length) return
    objectIds.forEach((item) =>
      points.push(AV.Object.createWithoutData(className, item))
    )
    return this.destroyAll(points)
  }
}

export default $Object
